package io.gitee.zhangbinhub.admin.workflow.controller.api

import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.interfaces.LogAdapter
import io.gitee.zhangbinhub.acp.cloud.annotation.AcpCloudDuplicateSubmission
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.common.api.WorkFlowApi
import io.gitee.zhangbinhub.admin.common.constant.ModuleFuncCode
import io.gitee.zhangbinhub.admin.common.vo.CustomerQueryPageVo
import io.gitee.zhangbinhub.admin.common.vo.InfoVo
import io.gitee.zhangbinhub.admin.resource.server.base.BaseResourceServerController
import io.gitee.zhangbinhub.admin.resource.server.po.ProcessHandlingPo
import io.gitee.zhangbinhub.admin.resource.server.po.ProcessQueryPo
import io.gitee.zhangbinhub.admin.resource.server.po.ProcessStartPo
import io.gitee.zhangbinhub.admin.resource.server.po.ProcessTerminationPo
import io.gitee.zhangbinhub.admin.resource.server.tools.TokenTools
import io.gitee.zhangbinhub.admin.resource.server.vo.*
import io.gitee.zhangbinhub.admin.workflow.constant.WorkFlowExpression
import io.gitee.zhangbinhub.admin.workflow.domain.WorkFlowDomain
import io.swagger.v3.oas.annotations.*
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.servlet.http.HttpServletResponse
import jakarta.validation.Valid
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*

/**
 * @author zhang by 10/06/2019
 * @since JDK 11
 */
@Validated
@RestController
@RequestMapping(WorkFlowApi.basePath)
@Tag(name = "工作流引擎")
class WorkFlowController @Autowired constructor(
    logAdapter: LogAdapter, private val tokenTools: TokenTools, private val workFlowDomain: WorkFlowDomain
) : BaseResourceServerController(logAdapter) {
    @Operation(summary = "启动流程", description = "启动指定的流程，并关联唯一业务主键")
    @ApiResponses(ApiResponse(responseCode = "201", description = "流程启动成功"))
    @PutMapping(value = [WorkFlowApi.start], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun start(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid processStartPo: ProcessStartPo
    ): ResponseEntity<InfoVo> = tokenTools.getUserInfoFromToken(bearerTokenAuthentication).let { tokenUserInfoVo ->
        if (CommonTools.isNullStr(tokenUserInfoVo.id)) {
            throw WebException("获取当前登录用户信息失败！")
        }
        workFlowDomain.startFlow(processStartPo, tokenUserInfoVo.id).let {
            ResponseEntity.status(HttpStatus.CREATED).body(InfoVo(message = it))
        }
    }

    @Operation(summary = "查询流程任务列表")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowAdmin)
    @GetMapping(value = [WorkFlowApi.taskList + "/{processInstanceId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun taskList(@PathVariable processInstanceId: String): ResponseEntity<List<ProcessTaskVo>> =
        workFlowDomain.findTaskList(processInstanceId).let {
            ResponseEntity.ok(it)
        }

    @Operation(summary = "查询待办任务", description = "获取当前用户的待办任务列表")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowPending)
    @PostMapping(value = [WorkFlowApi.pending], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun pending(@RequestBody @Valid processQueryPo: ProcessQueryPo): ResponseEntity<CustomerQueryPageVo<ProcessTaskVo>> =
        workFlowDomain.findTaskList(processQueryPo).let {
            ResponseEntity.ok(it)
        }

    @Operation(summary = "分配任务", description = "将任务指定给用户签收")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowAdmin)
    @PatchMapping(
        value = [WorkFlowApi.distribute + "/{taskId}/{userId}"], produces = [MediaType.APPLICATION_JSON_VALUE]
    )
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun distribute(
        @Parameter(description = "任务ID", required = true) @PathVariable taskId: String,
        @Parameter(description = "用户ID", required = true) @PathVariable userId: String
    ): ResponseEntity<InfoVo> = workFlowDomain.claimTask(taskId, userId).let {
        ResponseEntity.ok(InfoVo(message = "任务已分配"))
    }

    @Operation(summary = "领取任务", description = "签收指定的任务")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowProcess)
    @PatchMapping(value = [WorkFlowApi.claim + "/{taskId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun claim(
        @Parameter(
            description = "任务ID", required = true
        ) @PathVariable taskId: String
    ): ResponseEntity<InfoVo> = workFlowDomain.claimTask(taskId).let {
        ResponseEntity.ok(InfoVo(message = "任务已签收"))
    }

    @Operation(summary = "转办任务", description = "转办指定的任务")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowProcess)
    @PatchMapping(value = [WorkFlowApi.transfer + "/{taskId}/{userId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun transfer(
        @Parameter(description = "任务ID", required = true) @PathVariable taskId: String,
        @Parameter(description = "目标userId", required = true) @PathVariable userId: String
    ): ResponseEntity<InfoVo> = workFlowDomain.turnTask(taskId, userId).let {
        ResponseEntity.ok(InfoVo(message = "任务已转办"))
    }

    @Operation(summary = "委托办理任务", description = "委托办理指定的任务")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowProcess)
    @PatchMapping(
        value = [WorkFlowApi.delegate + "/{taskId}/{acceptUserId}"], produces = [MediaType.APPLICATION_JSON_VALUE]
    )
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun delegate(
        @Parameter(description = "任务ID", required = true) @PathVariable taskId: String,
        @Parameter(description = "接收userId", required = true) @PathVariable acceptUserId: String
    ): ResponseEntity<InfoVo> = workFlowDomain.delegateTask(taskId, acceptUserId).let {
        ResponseEntity.ok(InfoVo(message = "任务已委托办理"))
    }

    @Operation(summary = "流程处理", description = "可选通过或不通过")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowProcess)
    @PostMapping(value = [WorkFlowApi.process], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun process(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid processHandlingPo: ProcessHandlingPo
    ): ResponseEntity<InfoVo> = tokenTools.getUserInfoFromToken(bearerTokenAuthentication).let { tokenUserInfoVo ->
        if (CommonTools.isNullStr(tokenUserInfoVo.id)) {
            throw WebException("获取当前登录用户信息失败！")
        }
        workFlowDomain.processTask(processHandlingPo, tokenUserInfoVo.id!!).let {
            ResponseEntity.ok(InfoVo(message = "流程处理完成"))
        }
    }

    @Operation(summary = "流程强制结束")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowProcess)
    @DeleteMapping(value = [WorkFlowApi.termination], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun termination(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid processTerminationPo: ProcessTerminationPo
    ): ResponseEntity<InfoVo> = tokenTools.getUserInfoFromToken(bearerTokenAuthentication).let { tokenUserInfoVo ->
        if (CommonTools.isNullStr(tokenUserInfoVo.id)) {
            throw WebException("获取当前登录用户信息失败！")
        }
        workFlowDomain.findProcessInstance(processTerminationPo.processInstanceId!!).let { instance ->
            if (instance.finished) {
                false
            } else {
                when {
                    hasAuthentication(bearerTokenAuthentication, mutableListOf(ModuleFuncCode.flowAdmin)) -> {
                        true
                    }

                    else -> {
                        instance.startUser != null && !CommonTools.isNullStr(instance.startUser!!.id) && instance.startUser!!.id == tokenUserInfoVo.id
                    }
                }
            }
        }.let {
            if (it) {
                workFlowDomain.deleteProcessInstance(
                    processTerminationPo, tokenUserInfoVo.id
                ).let {
                    ResponseEntity.ok(InfoVo(message = "强制结束流程实例成功"))
                }
            } else {
                throw WebException("流程已结束或当前登录人不是流程发起人，无法终止该流程！")
            }
        }
    }

    @Operation(summary = "获取流程实例", description = "获取指定流程实例")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @GetMapping(value = [WorkFlowApi.instance + "/{processInstanceId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryInstance(
        @Parameter(description = "流程实例id", required = true) @PathVariable processInstanceId: String
    ): ResponseEntity<ProcessInstanceVo> = workFlowDomain.findProcessInstance(processInstanceId).let {
        ResponseEntity.ok(it)
    }

    @Operation(summary = "获取流程实例", description = "获取流程实例")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PostMapping(value = [WorkFlowApi.instance], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryInstance(@RequestBody @Valid processQueryPo: ProcessQueryPo): ResponseEntity<CustomerQueryPageVo<ProcessInstanceVo>> =
        workFlowDomain.findProcessInstance(processQueryPo).let {
            ResponseEntity.ok(it)
        }

    @Operation(summary = "获取我处理过的流程实例", description = "获取我处理过的流程实例")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PostMapping(value = [WorkFlowApi.myProcess], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryInstanceForMyProcess(@RequestBody @Valid processQueryPo: ProcessQueryPo): ResponseEntity<CustomerQueryPageVo<ProcessInstanceVo>> =
        workFlowDomain.findProcessInstanceForMyProcess(processQueryPo).let {
            ResponseEntity.ok(it)
        }

    @Operation(summary = "获取流程实例", description = "获取流程实例")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PostMapping(value = [WorkFlowApi.history], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryHistoryInstance(@RequestBody @Valid processQueryPo: ProcessQueryPo): ResponseEntity<CustomerQueryPageVo<ProcessInstanceVo>> =
        workFlowDomain.findHistoryProcessInstance(processQueryPo).let {
            ResponseEntity.ok(it)
        }

    @Operation(summary = "获取流程历史记录", description = "获取指定流程实例的历史处理记录")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @GetMapping(value = [WorkFlowApi.history + "/{processInstanceId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryHistoryActivity(
        @Parameter(description = "流程实例id", required = true) @PathVariable processInstanceId: String
    ): ResponseEntity<List<ProcessHistoryActivityVo>> = workFlowDomain.findHistoryActivity(processInstanceId).let {
        ResponseEntity.ok(it)
    }

    @Operation(summary = "获取流程任务信息", description = "获取指定流程任务")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；系统异常"))
    @PreAuthorize(WorkFlowExpression.flowProcess)
    @GetMapping(value = [WorkFlowApi.task + "/{taskId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun queryTaskInfo(
        @Parameter(description = "流程任务ID", required = true) @PathVariable taskId: String
    ): ResponseEntity<ProcessTaskVo> = workFlowDomain.findTaskById(taskId).let {
        ResponseEntity.ok(it)
    }

    @Operation(summary = "获取流程图", description = "获取指定实例id的流程图，返回图片二进制流数据")
    @ApiResponses(ApiResponse(responseCode = "200", description = "请求成功，进行文件下载；"))
    @GetMapping(value = [WorkFlowApi.diagram + "/{processInstanceId}/{imgType}"], produces = [MediaType.ALL_VALUE])
    @Throws(WebException::class)
    fun diagram(
        response: HttpServletResponse,
        @Parameter(description = "流程实例id", required = true) @PathVariable processInstanceId: String,
        @Parameter(description = "图片格式", example = "png;bmp;jpg;gif", required = true) @PathVariable imgType: String
    ) {
        response.contentType = "image/$imgType"
        workFlowDomain.generateDiagram(processInstanceId, imgType).transferTo(response.outputStream)
    }
}
